In this tutorial, we will discuss python operator overloading and also learn different magical methods which change the operator meaning.
What is Operator Overloading in Python?
In python, the same operator can perform different operations on different objects and there are many predefined operators in python. For example, the + operator can perform arithmetic addition between two numbers while it performs concatenation between two strings. Operator overloading is a technique of performing different operations with the same operator, on different objects. Python has inbuilt Operators overloading that means they are supposed to operate between the objects as per defined in the in-built classes.
Example:
class ClassName:
def __init__(self, a,b):
self.a=a
self.b=b
c = ClassName(10,20)
d= ClassName(40,50)
add = c+d
Output:
TypeError: unsupported operand type(s) for +: 'ClassName' and 'ClassName'
Behind the code
We got an error in the above code because the + operator has no pre-defined statement, how to operate between two classes objects, that’s why we got an unsupported operand type error.
Special function in Python:
In python the class methods, which name start and end with double underscore __ are known as a special function or magical methods. The class constructor __init__() is one of those special functions and there are many other special functions defined in python. Let’s discuss one more special function __str__() The __str__() method in class used to print some valuable information about the object when the user use the print(object_name) statement:
Example:
class ClassName:
def __init__(self, a,b):
self.a=a
self.b=b
def __str__(self):
return "This what you get when you print its object"
c = ClassName(10,20)
print("-------when we use the print(c) statement-------")
print(c) # it is equivalent to c.__str__()
print("\n -------when we use the c.__str__() statement------")
print(c.__str__())
Output:
-------when we use the print(c) statement-------
This what you get when you print its object
-------when we use the c.__str__() statement------
This what you get when you print its object
Behind the scene
In the above code, we can see that when we print the object of the class it print the statement of __str__() method.
Operator overloading with the + operator
We can overload the + operator using special function __add__() and can make the + operator to perform some specific task.
For example:
class ClassName:
def __init__(self, a,b):
self.a=a
self.b=b
def __str__(self):
return "This what you get when you print its object"
def __add__(self,next): # Here self is c_1 and next is c_2
a = self.a+next.a
b = self.b+next.b
return (a,b)
c_1 = ClassName(10,20)
c_2 = ClassName(30,20)
print("-------when we use the c_1 + c_2 statement-------")
print(c_1 + c_2) # it is equivalent to c_1.__add__(c2)
print("\n -------when we use the c_1.__add__(c_2) statement------")
print(c_1.__add__(c_2))
Output:
-------when we use the c_1 + c_2 statement-------
(40, 40)
-------when we use the c_1.__add__(c_2) statement------
(40, 40)
Behind the code
In the above code, we defined a special function __add__() which set the task for + operator. Like arithmetic + operator need two numbers to add, same here we created two object c_1 and c_2.
Arithmetic Special functions
Operation | Expression | Equivalent |
Addition | c_1 + c_2 | c_1.__add__(c_2) |
Subtraction | c_1 - c_2 | c_1.__sub__(c_2) |
Multiplication | c_1 * c_2 | c_1.__mul__(c_2) |
Power | c_1 ** c_2 | c_1.__pow__(c_2) |
Division | c_1 / c_2 | c_1.__truediv__(c_2) |
Floor Division | c_1 // c_2 | c_1.__floordiv__(c_2) |
Remainder | c_1 % c_2 | c_1.__mod__(c_2) |
Bitwise Left Shift | c_1 << c_2 | c_1.__lshift__(c_2) |
Bitwise Right Shift | c_1 >> c_2 | c_1.__rshift__(c_2) |
Bitwise AND | c_1 & c_2 | c_1.__and__(c_2) |
Bitwise OR | c_1 | c_2 | c_1.__or__(c_2) |
Bitwise XOR | c_1 ^ c_2 | c_1.__xor__(c_2) |
Bitwise NOT | ~c_2 | c_1.__invert__() |
Comparison Operator Overloading
Like arithmetic operator, we can overload comparison operators.
Operation | Expression | Equivalent |
Less than | c_1 < c_2 | c_1.__lt__(c_2) |
Less than or equal to | c_1 <= c_2 | c_1.__le__(c_2) |
Equal to | c_1 == c_2 | c_1.__eq__(c_2) |
Not equal to | c_1 != c_2 | c_1.__ne__(c_2) |
Greater than | c_1 > c_2 | c_1.__gt__(c_2) |
Greater than or equal to | c_1 >= c_2 | c_1.__ge__(c_2) |
Example:
class ClassName:
def __init__(self, a,b):
self.a=a
self.b=b
def __str__(self):
return "This what you get when you print its object"
def __add__(self,next):
a = self.a+next.a
b = self.b+next.b
return (a,b)
def __gt__(self,next):
if self.a + self.b > next.a+next.b:
return True
else:
return False
c_1 = ClassName(10,50)
c_2 = ClassName(30,10)
print("-------when we use the c_1 > c_2 statement-------")
print(c_1 > c_2) # it is equivalent to c_1.__gt__(c2)
print("\n -------when we use the c_1.__gt__(c_2) statement------")
print(c_1.__gt__(c_2))
Output:
-------when we use the c_1 > c_2 statement-------
True
-------when we use the c_1.__gt__(c_2) statement------
True